package main

import (
	"bytes"
	"fmt"
	"net/http"
	"sync"
	"time"
)

func main()  {
	//Go语言地并发模式
	//约束
	//不同的保证操作安全的方法：
	//用于共享内存的同步原语(如sync.Mutex)
	//通过通信共享内存来进行同步(如channel)
	//
	//隐式并发安全方法：
	//不会发生改变数据
	//受到保护的数据
	//
	//约束是一种确保了信息只能从一个并发过程中获取到的简单且强大的方法
	//特定约束
	//词法约束

	//特定约束示例：
	data := make([]int,4)

	loopData := func(handleData chan<- int) {
		defer close(handleData)
		for i := range data {
			handleData <- data[i]
		}
	}

	handleData := make(chan int)
	go loopData(handleData)

	for num := range handleData {
		fmt.Println(num)
	}


	//词法约束示例：
	chanOwner := func() <-chan int {
		results := make(chan int,5)    //在chanOwner函数词法范围类实例化channel
		go func() {
			defer close(results)
			for i:=0;i<=5;i++ {
				results <- i
			}
		}()
		return results
	}

	consumer := func(results <-chan int) { //收到一个int channel的只读副本，通过声明要求的唯一用法是读访问，将channel内的使用约束为只读
		for result := range results {
			fmt.Printf("Received: %d\n",result)
		}
		fmt.Println("Done receiving!")
	}

	results := chanOwner() //收到channel的读处理，将其传递给消费者
	consumer(results)


	//使用不是并发安全的数据结构的约束的例子,bytes.Buffer
	printData := func(wg *sync.WaitGroup,data []byte) {
		defer wg.Done()

		var buffer bytes.Buffer
		for _,b:=range data {
			fmt.Fprintf(&buffer,"%c",b)
		}
		fmt.Println(buffer.String)
	}

	var wg sync.WaitGroup
	wg.Add(2)
	data := []byte("golang")
	go printData(&wg,data[:3])
	go printData(&wg,data[3:])

	wg.Wait()




	//for-select 循环
	//	向channel发送迭代变量：
	//	将可迭代的内容转换为channel上的值：
		for _,s := range []string{"a","b","c"} {
			select {
			case <-done:
				return
			case stringStream <- s:
			}
		}

		//循环等待停止：
		//保持select语句尽可能短：
		//如果已经完成的channel未关闭，将退出select语句并继续执行for循环的其余部分
		for {
			select {
			case <-done:
				return
			default:
			}
		}

		//将工作嵌入到选择语句的默认子句中
		//当我们输入select语句时，如果完成的channel尚未关闭，将执行default子句
		for {
			select {
			case <-done:
				return
			default:
				//进行非抢占式任务
			}
		}

















	//防止goroutine泄露
	//goroutine不会被运行时垃圾回收
	//goroutine被终止的方式：
	//完成工作
	//不可恢复的错误导致不能继续工作
	//被告知需要终止工作
	//
	//一个简单的goroutine泄露：
	doWork := func(strings <-chan string) <-chan interface{} {
		completed := make(chan interface{})
		go func() {
			defer fmt.Println("doWork exited.")
			defer close(completed)
			for s := range strings {
				//做一些有趣的操作
				fmt.Println(s)
			}
		}()
		return completed
	}

	doWork(nil)
	//也许这里有其他操作需要进行
	fmt.Println("Done.")

	//将一个空的channel传给doWork,字符串channel永远不会写入任何字符串，包含doWork的goroutine将在此过程的整个生命周期中保留内存中
	//
	//
	//父goroutine将名为Done的只读channel传递给子goroutine，若想要取消子goroutine时关闭该channel
	doWork := func(
		done <-chan interface{},
		strings <-chan string,
	) <-chan interface{} { //将完成的channel传递给doWork函数
		terminated := make(chan interface{})
		go func() {
			defer fmt.Println("doWork exited.")
			defer close(terminated)
			for {
				select {
				case <-done:   //检查done channel是否已经发出信号，如有，从goroutine返回
					return
				}
			}
		}()
		return terminated
	}

	done := make(chan interface{})
	terminated := doWork(done,nil)

	go func() {    //创建另一个goroutine，若超过1s就会取消doWork中产生的goroutine
		//在1秒钟之后取消本操作
		time.Sleep(1 * time.Second)
		fmt.Println("Canceling doWork goroutine...")
		close(done)
	}()

	<-terminated   //加入从main goroutine的doWork中产生的goroutine位置
	fmt.Println("Done.")



	//处理一个goroutine阻塞向channel进行写入的请求：
	newRandStream := func() <-chan int {
		randStream := make(chan int)
		go func() {
			//无法告诉生产者可以停止，所以该语句永远不会运行
			defer fmt.Println("newRandStream closure exited.") //在goroutine成功终止时打印出一条消息

			defer close(randStream)
			for {
				randStream <- rand.Int()   // math/rand包
			}
		}()
		return randStream
	}
	randStream := newRandStream()
	fmt.Println("3 random ints:")
	for i:=1;i<=3;i++ {
		fmt.Printf("%d: %d\n",i,<-randStream)
	}


	//为生产者goroutine提供一个通知退出的channel
	newRandStream := func(done <-chan interface{}) <-chan int {
		randStream := make(chan int)
		go func() {
			defer fmt.Println("newRandStream closure exited.")
			defer close(randStream)
			for {
				select {
				case randStream <- rand.Int():
				case <-done:
					return
				}
			}
		}()
		return randStream
	}

	done := make(chan interface{})
	randStream := newRandStream(done)
	fmt.Println("3 random intes:")
	for i:=1;i<=3;i++ {
		fmt.Printf("%d:%d\n",i,<-randStream)
	}
	close(done)

	//模拟正在运行的工作
	time.Sleep(1 * time.Second)


	//如果goroutine负责创建goroutine，则也负责确保它可以停止goroutine
	//
	//
	//or-channel
	//将一个或多个完成的channel合并到一个完成的channel中，该channel在任何组件channel关闭时关闭
	//
	//通过递归和goroutine创建一个符合done channel
	var or func(channels ...<-chan interface{}) <-chan interface{}
	or = func(channels ...<-chan interface{}) <-chan interface{} { //有函数，或采用可变的channel切片并返回单个channel
		switch len(channels) {
		case 0:       //设置终止标准，切片空，返回空channel，
			return nil
		case 1:       //终止标准，切片只有一个元素，只返回该元素
			return channels[0]
		}

		orDone := make(chan interface{})
		go func() {       //函数主体，递归位置，创建一个goroutine，以便可以不受阻塞的等待channel上消息
			defer close(orDone)

			switch len(channels) {
			case 2:       //基于迭代方式，每次迭代调用至少有两个channel，此处为需要两个channel的情况采用约束goroutine数据的优化方法
				select {
				case <-channels[0]:
				case <-channels[1]:
				}
			default:      //循环到存放所有channel的slice第三个索引，创建一个or-channel并从此channel中选择一个。。。。。。
				select {
				case <-channels[0]:
				case <-channels[1]:
				case <-channels[2]:
				case <-or(append(channels[3:],orDone)...):
				}
			}
		}()
		return orDone
	}

	//可以将任意数量的channel组合到单个channel中，只要任何组件channel关闭或写入，该channel就会关闭
	//经过一段时间后关闭的channel，并将这些channel合并到一个关闭的单个channel中
	sig := func(after time.Duration) <-chan interface{}{   //创建一个channel，当后续时间中指定的时间结束时将关闭该channel
		c := make(chan interface{})
		go func() {
			defer close(c)
			time.Sleep(after)
		}()
		return c
	}
	start := time.Now()    //大致追踪来自or函数的channel何时开始阻塞
	<-or(
		sig(2*time.Hour),
		sig(5*time.Minute),
		sig(1*time.Second),
		sig(1*time.Hour),
		sig(1*time.Minute),
	)
	fmt.Printf("done after %v",time.Since(start))  //打印读取发生的时间



	//错误处理
	//示例1：
	checkStatus := func(
		done <-chan interface{},
		urls ...string,
	) <-chan *http.Response {
		response := make(chan *http.Response)
		go func() {
			defer close(response)
			for _,url := range urls{
				resp,err := http.Get(url)
				if err != nil {
					fmt.Println(err)
					continue
				}
				select {
				case <-done:
					return
				case response <- resp:
				}
			}
		}()
		return response
	}
	done := make(chan interface{})
	defer close(done)
	urls := []string{"https://www.google.com","https://badhost"}
	for response := range checkStatus(done,urls...) {
		fmt.Printf("Response: %v\n",response.Status)
	}


	//示例2：
	type Result struct {   //创建一个包含* http.Response和从goroutine中的循环迭代中可能出现的错误的类型
		Error error
		Response *http.Response
	}
	checkStatus := func(done <-chan interface{},urls ...string)<-chan Result { //返回一个可读取的channel，以检索循环迭代的结果
		results := make(chan Result)
		go func() {
			defer close(results)
			for _,url := range urls {
				var result Result
				resp,err := http.Get(url)
				result = Result{Error: err,Response: resp} //创建一个Result实例，并设置错误和响应字段
				select {
				case <-done:
					return
				case results <- result:    //将结果写入channel的位置
				}
			}
		}()
		return results
	}
	done := make(chan interface{})
	defer close(done)
	urls := []string{"https://www.google.com","https://badhost"}
	for result := range checkStatus(done,urls...) {
		if result.Error != nil {   //在main goroutine中，能够智能的处理由checkStatus启动的goroutine中出现的错误，以及更大程序的完整背景
			fmt.Printf("error: %v",result.Error)
			continue
		}
		fmt.Printf("Response: %v\n",result.Response.Status)
	}


	//实例3：
	//当出现三个或更多错误时停止尝试检查状态
	type Result struct {   //创建一个包含* http.Response和从goroutine中的循环迭代中可能出现的错误的类型
		Error error
		Response *http.Response
	}
	checkStatus := func(done <-chan interface{},urls ...string)<-chan Result { //返回一个可读取的channel，以检索循环迭代的结果
		results := make(chan Result)
		go func() {
			defer close(results)
			for _,url := range urls {
				var result Result
				resp,err := http.Get(url)
				result = Result{Error: err,Response: resp} //创建一个Result实例，并设置错误和响应字段
				select {
				case <-done:
					return
				case results <- result:    //将结果写入channel的位置
				}
			}
		}()
		return results
	}

	done := make(chan interface{})
	defer close(done)

	errCount := 0
	urls := []string{"a","https://www.google.com","b","c","d"}
	for result := range checkStatus(done,urls...) {
		if result.Error != nil {
			fmt.Printf("error: %v\n",result.Error)
			errCount++
			if errCount >= 3 {
				fmt.Println("Too many errors,breaking!")
				break
			}
			continue
		}
		fmt.Printf("Response: %v\n",result.Response.Status)
	}




}




















